﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DconManagerViewModel.cs" company="R.V.Kasner">
//   Copyright © 2011 R.V.Kasner
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace Owen.Terminal.ViewModel.DconManager
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    using GalaSoft.MvvmLight.Command;

    using Microsoft.Practices.Prism.Events;

    using Owen.Communication.Protocols;
    using Owen.Communication.Utility;
    using Owen.Terminal.Commons;
    using Owen.Terminal.Events;
    using Owen.Terminal.View.DconManager;

    public sealed class DconManagerViewModel : ViewModelBase<IDconManagerView>, IDconManagerViewModel
    {
        private const int ReadDeviceNameChar = 0x4D; // 'M'

        private const int ReadProgrammVersionChar = 0x46; // 'F'

        private readonly IEnumerable<DconPackageTypes> requestTypes;

        private int address;

        private int chanel;

        private string output;

        private DconPackageTypes selectedRequestType;

        // in design time
        public DconManagerViewModel()
        {
            this.address = 15;
            this.chanel = 7;
            this.output = "какието данные +12.05+483+";
            this.requestTypes = new[]
                {
                    DconPackageTypes.ReadBatchData, DconPackageTypes.ReadChanelData, DconPackageTypes.ReadDeviceName, 
                    DconPackageTypes.ReadProgrammVersion
                };
        }

        public DconManagerViewModel(IDconManagerView view, IEventAggregator evntAgg)
            : base(view)
        {
            this.address = 1;
            this.chanel = 1;
            this.output = null;
            this.requestTypes = new[]
                {
                    DconPackageTypes.ReadBatchData, DconPackageTypes.ReadChanelData, DconPackageTypes.ReadDeviceName, 
                    DconPackageTypes.ReadProgrammVersion
                };

            var cmnds = new[]
                {
                    new VisualCommand
                        {
                            Command =
                                new RelayCommand(() => evntAgg.GetEvent<TransmitStartEvent>().Publish(this.GetHead())), 
                            Title = "Запросить данные", 
                        }, 
                    new VisualCommand
                        {
                            Command =
                                new RelayCommand(this.ClearLog), 
                            Title = "Очистить журнал DCON", 
                        }, 
                };
            this.View.Selected += () => evntAgg.GetEvent<VisualCommandsChangeEvent>().Publish(cmnds);

            evntAgg.GetEvent<ReceivePackageEvent>().Subscribe(this.OnResponse);
        }

        public int Address
        {
            get
            {
                return this.address;
            }

            set
            {
                this.address = value;
                this.RaisePropertyChanged("Address");
            }
        }

        public int Chanel
        {
            get
            {
                return this.chanel;
            }

            set
            {
                this.chanel = value;
                this.RaisePropertyChanged("Chanel");
            }
        }

        public string Output
        {
            get
            {
                return this.output;
            }
        }

        public IEnumerable<DconPackageTypes> RequestTypes
        {
            get
            {
                return this.requestTypes;
            }
        }

        public DconPackageTypes SelectedRequestType
        {
            get
            {
                return this.selectedRequestType;
            }

            set
            {
                this.selectedRequestType = value;
                this.RaisePropertyChanged("SelectedRequestType");
            }
        }

        public void ClearLog()
        {
            this.output = string.Empty;
            this.RaisePropertyChanged("Output");
        }

        private TransmitData GetHead()
        {
            var data = new List<int>();
            string message;
            var isCommand = false;

            switch (this.SelectedRequestType)
            {
                case DconPackageTypes.ReadBatchData:
                    data.Add((byte)DconMarkers.StartReqvestData);
                    data.Add(this.address);
                    message = "Групповое чтение данных по адресу " + this.address;
                    break;

                case DconPackageTypes.ReadChanelData:
                    data.Add((byte)DconMarkers.StartReqvestData);
                    data.Add(this.address);
                    data.Add(this.chanel);
                    message = "Чтение данных из канала " + this.chanel + " по адресу " + this.address;
                    break;

                case DconPackageTypes.ReadDeviceName:
                    data.Add((byte)DconMarkers.StartReqvestProperty);
                    data.Add(this.address);
                    data.Add(ReadDeviceNameChar);
                    isCommand = true;
                    message = "Чтение имени усройсва по адресу " + this.address;
                    break;

                case DconPackageTypes.ReadProgrammVersion:
                    data.Add((byte)DconMarkers.StartReqvestProperty);
                    data.Add(this.address);
                    data.Add(ReadProgrammVersionChar);
                    isCommand = true;
                    message = "Чтение версии программы по адресу " + this.address;
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
            }

            var package = Chef.DconBus.CreatePackage(data.ToArray(), isCommand);
            this.WriteToOutput(true, message);
            if (package != null && package.Length != 0)
            {
                var transData = new TransmitData(new HermitTransferHead(package), false);
                return transData;
            }

            throw new InvalidOperationException("Ошибка алгоритма DCON, был создан некоректный пакет DCON.");
        }

        private void OnResponse(int[] obj)
        {
            if (!Chef.DconBus.CheckAccessory(obj))
            {
                return;
            }
            
            var data = Chef.DconBus.GetData(obj);
            IEnumerable<int> values;
            string valueStr;
            byte[] bytes;

            switch ((DconMarkers)obj[0])
            {
                case DconMarkers.StartReqvestData:
                    return;

                case DconMarkers.StartReqvestProperty:
                    return;

                case DconMarkers.StartResponseData:
                    values = data.Skip(1);
                    bytes = values.Select((v, i) => Convert.ToByte(v & 0xff)).ToArray();
                    valueStr = Encoding.ASCII.GetString(bytes);
                    this.WriteToOutput(false, "Пакет с групповыми/канальными данными: " + valueStr);
                    break;

                case DconMarkers.StartResponseProperty:
                    values = data.Skip(3);
                    bytes = values.Select((v, i) => Convert.ToByte(v & 0xff)).ToArray();
                    valueStr = Encoding.ASCII.GetString(bytes);
                    var dadr = new[] { data[1], data[2] };
                    var adr = DataConverter.FromTetradCodesToDataBytes(dadr, DataConverter.AsciiCharToByte);
                    this.WriteToOutput(false, "Пакет от устройства " + adr[0] + " с метаданными: " + valueStr);
                    break;

                case DconMarkers.StartResponseError:
                    this.WriteToOutput(false, "Устройство ответило пакетом ошибки.");
                    return;

                default:
                    this.WriteToOutput(false, "Низвестный тип пакета.".ToUpper());
                    break;
            }
        }

        private void WriteToOutput(bool isRequest, string message)
        {
            var inout = isRequest ? "OUT" : " IN";
            var item = string.Format("{0:HH:mm:ss.fff}   {1}   {2}{3}", DateTime.Now, inout, message, Environment.NewLine);
            this.output += item;
            this.RaisePropertyChanged("Output");
        }
    }
}